#!/usr/bin/perl 
require 5.003;
use strict;
use Socket;

my ($port, $protocol, $paddr, $msg, $msg2, $pid, $length, $type, $client_addr, $remote, $iaddr, $i, @socks, $socket, $welcome, $total_pkts, $PKT_INTER);
$port       = 20;
$protocol = getprotobyname('tcp');
$PKT_INTER  = 30;

sub process_sig
{
	my $signo = shift;
	print ("Catch $signo.\n");
}

sub process_hup
{
#	open FILE, "./SP.cfg" || return;
#	while (<FILE>)
#	{
#		chomp;
#		$iaddr = inet_aton($remote) || die "no host: $remote";
#		$paddr = sockaddr_in($port, $iaddr);
#		if (!socket($socks[$total], AF_INET, SOCK_STREAM, $protocol))
#		{
#			print "socket: $!"; next;
#		}
#		if (!connect($socks[$total], $paddr))
#		{
#			 print "connect: $!"; next;
#		}
#		$total++;
#	}
#	close FILE;
	print "Catch SIGHUP\n";
}
$SIG{ALRM} = \&process_sig;
$SIG{HUP} = \&process_hup;
$SIG{CHLD} = 'IGNORE';
$SIG{PIPE} = 'IGNORE';

$paddr = sockaddr_in ($port, INADDR_ANY);

socket (SERVER, AF_INET, SOCK_STREAM, $protocol) || die "socket: $!";
bind (SERVER, $paddr)
     or die "bind: $!";
listen (SERVER, SOMAXCONN)
     or die "listen: $!";

print "SERVER started on port $port\n";

while (1)
{
	$client_addr = accept (CLIENT, SERVER);
	next if (!defined ($client_addr));
	($port, $iaddr) = sockaddr_in($client_addr);
	print "Capture server connected from ".inet_ntoa ($iaddr).":$port.\n";
	if (($pid = fork) == 0)
	{
		close SERVER;
		$port = 20;
		binmode CLIENT;
		foreach $remote (@ARGV)
		{
			$iaddr = inet_aton($remote) || die "no host: $remote";
			$paddr = sockaddr_in($port, $iaddr);
			$i++;
			if (!socket($socks[$i], AF_INET, SOCK_STREAM, $protocol))
			{
				print "socket: $!\n"; next;
			}
			eval
			{
				alarm (5);
				if (!connect($socks[$i], $paddr))
				{
					 print "connect: $!\n"; alarm (0); close ($socks[$i]); $socks[$i] = undef; next;
				}
				alarm (0);
			};
			setsockopt($socks[$i], SOL_SOCKET, SO_KEEPALIVE,  pack("l", 1));
			setsockopt($socks[$i], SOL_SOCKET, SO_LINGER, pack("II",1,0));
			if ($@)
			{
			 	print "connect: $!\n"; close $socks[$i]; next;
			}
			print "Connection succeed for $remote.\n";
		}
		setsockopt(CLIENT, SOL_SOCKET, SO_KEEPALIVE,  pack("l", 1));
		setsockopt(CLIENT, SOL_SOCKET, SO_LINGER, pack("II",1,0))
					or die "Can't set SO_LINGER: $!";
		while (read (CLIENT, $msg, 5))
		{
			($length, $type) = unpack ("iC", $msg);
			last if (
			   $length > 1024000
			   || (!defined ($i = read (CLIENT, $msg, $length - 5)))
			   || $i == 0
		   	);
			$total_pkts++;
			$msg2 = pack ("iCa*", $length, $type, $msg);
			for ($i=0; $i<=$#ARGV; $i++)
			{
				if (!defined ($socket = $socks[$i]))
				{
					next if (($total_pkts % $PKT_INTER) != 0);
					$remote = $ARGV[$i];
					$iaddr = inet_aton($remote);
					$paddr = sockaddr_in($port, $iaddr);
					if (!socket($socket, AF_INET, SOCK_STREAM, $protocol))
					{
						print "socket: $!\n"; next;
					}
					eval
					{
						alarm (5);
						if (!connect($socket, $paddr))
						{
					 		print $ARGV[$i].": connect: $!\n"; alarm (0); close ($socket); next;
						}
						alarm (0);
					};
					setsockopt($socket, SOL_SOCKET, SO_KEEPALIVE,  pack("l", 1));
					setsockopt($socket, SOL_SOCKET, SO_LINGER, pack("II",1,0));
					if ($@)
					{
					 	print "connect: $!\n"; close ($socket); next;
					}
					if (!defined (syswrite ($socket, $welcome)))
					{
						print "syswrite: $!\n"; close ($socket); next;
					}
					$socks[$i] = $socket;
				}

				eval
				{
					alarm (5);
				if (!defined (syswrite ($socket, $msg2)))
				{
					print $ARGV[$i].": Here $!\n";
					close $socket;
					$socks[$i] = undef;
				}
					alarm (0);
				};
#				if ($type == 0)
#				{
#					read ($socket, $msg, 9);
#				}
			}
			if ($type == 0)
			{
				$welcome = $msg2;
				$msg = pack ('iCi', 9, 100, 0);
				syswrite (CLIENT, $msg);
			}
		}
		close CLIENT;
		exit (0);
	} else
	{
		close CLIENT;
	}
}
close SERVER;
